#include "PlayState.h"

PlayState::PlayState(GameContext& gc)
	: GameState(gc)
	, mMap()
	, mGuardian()
	, background()
	, explosionSound()
	, fireSound()
	, impactSound()
	, theme01()
	, theme02()
	, theme03()
	, theme04()
	, kMaxBullets(10)
	, bulletIndex(0)
	, kMaxEnemies(12)
	, enemyIndex(0)
	, bossHealth(3)
	, kMaxExplosions(10)
	, explosionIndex(0)
	, mGuardianHealth()
	, bullets(new Bullet[kMaxBullets])
	, mEnemy(new Enemy[kMaxEnemies])
	, explosions(new SGE_Emitter[kMaxExplosions])
	, mBoss()
	, collisionDelay(0)
	, quit(false)
{
}

PlayState::~PlayState(void)
{
}

// updates guardian, bullets, enemies, explosions
void PlayState:: UpdateGameObjects(float deltaTime)
{
	// updating fighter
	mGuardian.Update(deltaTime, mMap);

	// update bullets
	for (int i = 0; i < kMaxBullets; ++i )
		bullets[i].Update(deltaTime);

	// update enemies
	for (int i = 0; i < kMaxEnemies; ++i )
		mEnemy[i].Update(deltaTime, mMap);

	// update boss
	mBoss.Update(deltaTime, mMap);

	// update explosions
	for (int i = 0; i < kMaxExplosions; ++i )
		explosions[i].Update(deltaTime);

	// update map
	mMap.Update(deltaTime);
}

// fire bullets
void PlayState:: FireBullets()
{
	// if space is pressed, fire bullets
	if (Input_IsKeyPressed(Keys::SPACE))
	{
		if (!bullets[bulletIndex].IsActive() )
		{
			SVector2 pos = mGuardian.GetPosition();
			SVector2 offset(22.0f, -105.0f);
			SVector2 vel(800.0f, 0.0f);
			if (mGuardian.IsFacingLeft())
			{
				offset.x *= -1.0f;
				vel.x *= -1.0f;
			}
			bullets[bulletIndex].Fire(pos + offset, vel);
			bulletIndex = (bulletIndex + 1) % kMaxBullets;
		}

		// bullet sound
		fireSound.Play();
	}
}

// checks collision - bullet vs enemy
void PlayState:: BulletEnemyCollision(float deltaTime)
{
	// check bullets against enemies
	for (int e = 0; e < kMaxEnemies; ++e)
	{
		if ( mEnemy[e].IsActive() )
		{
			for (int b = 0; b < kMaxBullets; ++b)
			{
				if (bullets[b].IsActive())
				{
					SCircle eCircle = mEnemy[e].GetBoundingCircle();
					SCircle bCircle = bullets[b].GetBoundingCircle();
					float xDiff = eCircle.center.x - bCircle.center.x;
					float yDiff = eCircle.center.y - bCircle.center.y;
					float dist = sqrt((xDiff * xDiff) + (yDiff * yDiff));
					float radii = eCircle.radius + bCircle.radius;

					if ( dist <= radii )
					{
						bullets[b].Kill();
						mEnemy[e].Kill();

						explosions[explosionIndex].SetPosition(bCircle.center);
						explosions[explosionIndex].Start(100.0f, true);
						explosionIndex = (explosionIndex + 1) % kMaxExplosions; 

						// explosion sound
						explosionSound.Play();

						break;
					}
				}
			}
		}
	}

	// check bullets against boss
	if ( mBoss.IsActive() )
	{
		for (int b = 0; b < kMaxBullets; ++b)
		{
			if (bullets[b].IsActive())
			{
				SCircle eCircle = mBoss.GetBoundingCircle();
				SCircle bCircle = bullets[b].GetBoundingCircle();
				float xDiff = eCircle.center.x - bCircle.center.x;
				float yDiff = eCircle.center.y - bCircle.center.y;
				float dist = sqrt((xDiff * xDiff) + (yDiff * yDiff));
				float radii = eCircle.radius + bCircle.radius;

				if ( dist <= radii )
				{
					bullets[b].Kill();
					bossHealth--;

					explosions[explosionIndex].SetPosition(bCircle.center);
					explosions[explosionIndex].Start(100.0f, true);
					explosionIndex = (explosionIndex + 1) % kMaxExplosions; 

					// explosion sound
					explosionSound.Play();

					break;
				}
			}

			if ( bossHealth == 0 )
				mBoss.Kill();
		}		
	}
}

// checks collision - bullet vs world
void PlayState:: BulletWorldCollision(float deltaTime)
{
	// Check bullets against world
	for (int b = 0; b < kMaxBullets; ++b)
	{
		if (bullets[b].IsActive())
		{
			SCircle bCircle = bullets[b].GetBoundingCircle();
			if (mMap.IsColliding(bCircle.center))
			{
				bullets[b].Kill();

				explosions[explosionIndex].SetPosition(bCircle.center);
				explosions[explosionIndex].Start(30.0f, true);
				explosionIndex = (explosionIndex + 1) % kMaxExplosions; 

				// explosion sound
				explosionSound.Play();
			}
		}
	}
}

// checks collision - guardian vs enemy
void PlayState:: GuardianEnemyCollision(float deltaTime)
{
	// check guardian against enemies
	for (int e = 0; e < kMaxEnemies; ++e)
	{
		if ( mEnemy[e].IsActive() )
		{
			SCircle eCircle = mEnemy[e].GetBoundingCircle();
			SCircle gCircle = mGuardian.GetBoundingCircle();
			float xDiff = eCircle.center.x - gCircle.center.x;
			float yDiff = eCircle.center.y - gCircle.center.y;
			float dist = sqrt((xDiff * xDiff) + (yDiff * yDiff));
			float radii = eCircle.radius + gCircle.radius;

			collisionDelay -= deltaTime;

			if ( dist <= radii )
			{
				//spawnDelay -= deltaTime;
				if (collisionDelay <= 0.0f)
				{
					mGuardianHealth.Damage(2);
					impactSound.Play();
					collisionDelay = 5;
					break;
				}
			}
		}
	}
	
	// check guardian against enemies
	if ( mBoss.IsActive() )
	{
		SCircle eCircle = mBoss.GetBoundingCircle();
		SCircle gCircle = mGuardian.GetBoundingCircle();
		float xDiff = eCircle.center.x - gCircle.center.x;
		float yDiff = eCircle.center.y - gCircle.center.y;
		float dist = sqrt((xDiff * xDiff) + (yDiff * yDiff));
		float radii = eCircle.radius + gCircle.radius;

		collisionDelay -= deltaTime;

		if ( dist <= radii )
		{
			//spawnDelay -= deltaTime;
			if (collisionDelay <= 0.0f)
			{
				mGuardianHealth.Damage(4);
				impactSound.Play();
				collisionDelay = 5;
			}
		}
	}
}

// checks collision
void PlayState:: CheckCollision(float deltaTime)
{
	BulletEnemyCollision(deltaTime);
	BulletWorldCollision(deltaTime);
	GuardianEnemyCollision(deltaTime);
}

// load sound
void PlayState:: LoadSound()
{
	// load explosion sound
	explosionSound.Load("explosion.wav");

	// load fire sound
	fireSound.Load("photongun1.wav");

	// load impact sound
	impactSound.Load("impact.mp3");

	// load and play music for each specific level
	if ( mGameContext.GetLevel() == 1 )
	{
		theme01.Load("theme01.mp3");
		theme01.Play(true);
	}

	else if ( mGameContext.GetLevel() == 2 )
	{
		theme02.Load("theme02.mp3");
		theme02.Play(true);
	}

	else if ( mGameContext.GetLevel() == 3 )
	{
		theme03.Load("theme03.mp3");
		theme03.Play(true);
	}

	else if ( mGameContext.GetLevel() == 4 )
	{
		theme04.Load("theme04.mp3");
		theme04.Play(true);
	}
}

// unload sound
void PlayState:: UnloadSound()
{
	// unload explosion sound
	explosionSound.Unload();

	// unload fire sound
	fireSound.Unload();

	// unload impact sound
	impactSound.Unload();

	// unload themes
	theme01.Unload();
	theme02.Unload();
	theme03.Unload();
	theme04.Unload();

	// stop music
	theme01.Stop();
	theme02.Stop();
	theme03.Stop();
	theme04.Stop();
}

// debug test for bounding circles
void PlayState:: RenderBoundingCircles()
{
	Graphics_DebugCircle(mGuardian.GetBoundingCircle(), 0xff0000);

	for (int i = 0; i < kMaxEnemies; ++i )
		 Graphics_DebugCircle(mEnemy[i].GetBoundingCircle(), 0xff0000);

	Graphics_DebugCircle(mBoss.GetBoundingCircle(), 0xff0000);
}

// --------------------------------------------------------------------------------
// --------------------------------------------------------------------------------
//
//					LOAD ----- UNLOAD ----- UPDATE ----- RENDER
//
// --------------------------------------------------------------------------------
// --------------------------------------------------------------------------------

void PlayState:: Load()
{
	const int kWinWidth = IniFile_GetInt("WinWidth", 800);
	const int kWinHeight = IniFile_GetInt("WinHeight", 600);

	// load background
	char backgroundName[256];
	sprintf_s(backgroundName, 256, "background_0%d.jpg", mGameContext.GetLevel());
	background.Load(backgroundName);

	// load map
	char levelName[256];
	char texturePackName[256];
	sprintf_s(levelName, 256, "level_0%d.txt", mGameContext.GetLevel());
	sprintf_s(texturePackName, 256, "texturepack_0%d.txt", mGameContext.GetLevel());
	mMap.Load(levelName, texturePackName);

	// load guardian
	mGuardian.Load();

	// set guardian initial position
 	SVector2 startPosition(kWinWidth * 0.1f, (int)(kWinHeight * 0.3f));
	mGuardian.SetPosition(startPosition);

	// load bullets
	for (int i = 0; i < kMaxBullets; ++i )
		bullets[i].Load();

	// enemy spawn locations
	std:: vector <SVector2> SpawnLocations = mMap.GetSpawnLocations();

	// load enemies
	for (int i = 0; i < kMaxEnemies; ++i )
	{
		mEnemy[i].Load(mGameContext.GetLevel());
		mEnemy[i].SetPosition(SpawnLocations[i]);
	}

	// boss spawn location
	std:: vector <SVector2> bSpawnLocation = mMap.GetBSpawnLocation();

	// load boss
	mBoss.Load(mGameContext.GetLevel());
	mBoss.SetPosition(bSpawnLocation[0]);

	// load explosions
	for (int i = 0; i < kMaxExplosions; ++i )
		explosions[i].Load("explosion.txt");

	// load all sound
	LoadSound();

	// load health
	mGuardianHealth.Load();
}

void PlayState:: Unload()
{
	// unload background
	background.Unload();

	// unload map
	mMap.Unload();

	// unload guardian
	mGuardian.Unload();

	// unload bullets
	for (int i = 0; i < kMaxBullets; ++i )
		bullets[i].Unload();

	// unload enemies
	for (int i = 0; i < kMaxEnemies; ++i )
		mEnemy[i].Unload();

	mBoss.Unload();

	// unload explosions
	for (int i = 0; i < kMaxExplosions; ++i )
		explosions[i].Unload();

	// unload all sound
	UnloadSound();

	// unload health
	mGuardianHealth.Unload();
}

NextState PlayState:: Update(float deltaTime)
{
	// updates guardian, bullets, map, 
	// enemies, explosions
	UpdateGameObjects(deltaTime);

	// fire bullets
	FireBullets();

	// check bullet collision
	CheckCollision(deltaTime);

	// transitioning between states
	NextState nextState = None;
	if (Input_IsKeyPressed(Keys::ESCAPE))
		nextState = Frontend;
	
	// if guardian falls below window
	if( (mGameContext.GetLevel() == 3) && (mGuardian.GetPosition().y >= (IniFile_GetFloat("WinHeight", 600) + 150.0f)))
		nextState = GameOver;

	// if guardian dies
	if( mGuardianHealth.GetHealth() <= 0 )
		nextState = GameOver;

	// if boss dies
	if( bossHealth <= 0 )
		nextState = Win;

	return nextState;
}

void PlayState:: Render()
{
	// render background
	background.Render();

	// render map --------------------------------------
	// getting the width and height
	const int kWinWidth = IniFile_GetInt("WinWidth", 800);
	const int kWinHeight = IniFile_GetInt("WinHeight", 600);

	SVector2 viewTarget = mGuardian.GetPosition();
	SVector2 viewOffset;

	viewOffset.x = (viewTarget.x - (kWinWidth * 0.5f));
	viewOffset.y = (viewTarget.y - (kWinHeight * 0.5f));

	// screen viewing limits
	SVector2 limit;
	limit.x = (float) mMap.GetWidth() - kWinWidth;
	limit.y = (float) mMap.GetHeight() - kWinHeight;

	// cannot be smaller than 0, or bigger than limit
	viewOffset.x = Clamp(viewOffset.x, 0.0f, limit.x);
	viewOffset.y = Clamp(viewOffset.y, 0.0f, limit.y);

	mMap.Render(viewOffset);
	// ------------------------------------------------

	// render enemies
	for (int i = 0; i < kMaxEnemies; ++i )
		mEnemy[i].Render(viewOffset);

	// render boss
	mBoss.Render(viewOffset);

	// render bullets
	for (int i = 0; i < kMaxBullets; ++i )
		bullets[i].Render(viewOffset);

	// render guardian
	mGuardian.Render(viewOffset);

	// render explosions
	for (int i = 0; i < kMaxExplosions; ++i )
	{
		// true = additive blending
		explosions[i].SetOffset(-viewOffset);
		explosions[i].Render(true);
	}

	// render health bar
	mGuardianHealth.Render(viewOffset);

	// debug test for bounding cirlces
	// RenderBoundingCircles();
}